package data

import (
	"context"
	"errors"
	"fmt"
	"rosesservice/pkg/util"
	"strconv"
	"strings"
	"time"

	v1 "gitlab.cqcb.com/shangyou_mic/dscm-approve-api-pb/api/roses/v1"
	"rosesservice/internal/biz"

	"gorm.io/gorm"
)

type Account struct {
	db      *DB
	rbac    *RbacSchema
	account *AccountSchema
}

func NewAccount(
	db *DB,
	rbac *RbacSchema,
	account *AccountSchema,
) (biz.AccountRepo, error) {
	return &Account{
		db:      db,
		rbac:    rbac,
		account: account,
	}, nil
}

func (s *Account) ServerGetUser(ctx context.Context, req *v1.FxUser) (*v1.FxUser, error) {
	user, err := s.account.ServerGetUser(ctx, req)
	if err != nil {
		return nil, err
	}
	if user == nil {
		return &v1.FxUser{}, nil
	}
	result := &v1.FxUser{
		Id:             user.Id,
		Category:       user.Category,
		Mobile:         user.Mobile,
		Username:       user.Username,
		Email:          user.Email,
		PasswordSalt:   user.PasswordSalt,
		Password:       user.Password,
		Integral:       user.Integral,
		RealName:       user.RealName,
		IdCardNo:       user.IdCardNo,
		BankNo:         user.BankNo,
		BankOpen:       user.BankOpen,
		Note:           user.Note,
		IsInternal:     int32(user.IsInternal),
		State:          user.State,
		StateReason:    user.StateReason,
		CreatedAt:      user.CreatedAt,
		UpdatedAt:      user.UpdatedAt,
		ReturnNormalAt: user.ReturnNormalAt,
		IsVerifier:     user.IsVerifier,
	}
	return result, nil
}

func (s *Account) ServerSaveUser(ctx context.Context, req *v1.FxUser) (*v1.FxUser, error) {
	req.IsVerifier = 1
	db := s.db.DB()
	timestamp := util.Timestamp()
	users := make([]*User, 0)
	db.Model(&User{}).Where("user_id = ?", req.UserId).Limit(1).Find(&users)
	if len(users) == 0 {
		user := &User{
			Category:    1,
			Mobile:      req.Mobile,
			Username:    req.Username,
			Email:       req.Email,
			Integral:    0,
			RealName:    req.RealName,
			State:       1,
			StateReason: "正常",
			IsVerifier:  1,
			UserId:      req.UserId,
		}
		user.CreatedAt = timestamp
		user.UpdatedAt = timestamp
		err := db.Model(&User{}).Create(user).Error
		if err != nil {
			return nil, err
		}
		req.Id = user.Id
		return req, nil
	}
	req.Id = users[0].Id
	req.State = users[0].State
	req.StateReason = users[0].StateReason
	return req, nil
}

func (s *Account) ServerSystemRole(ctx context.Context, req *v1.ServerSystemRoleRequest) (*v1.ServerSystemRoleReply, error) {
	result := &v1.ServerSystemRoleReply{}
	table := &RbacRole{}
	timestamp := time.Now().Unix()
	err := s.db.Transaction(nil, func(tx *gorm.DB) error {
		tx = tx
		for _, v := range req.Lists {
			w := &RbacRole{}
			err := tx.Model(table).Where("identifier = ?", v.Identifier).Select("id").First(w).Error
			if err != nil {
				if !errors.Is(err, gorm.ErrRecordNotFound) {
					return err
				}
				w.Name = v.Name
				w.Identifier = v.Identifier
				w.Note = v.Note
				w.State = v.State
				w.SortNumber = v.SortNumber
				w.CreatedAt = timestamp
				w.UpdatedAt = timestamp
				err = tx.Model(w).Create(w).Error
				if err != nil {
					return err
				}
				result.Total++
			}
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	return result, nil
}

func (s *Account) ServerGetUserPermitLists(ctx context.Context, req *v1.FxUser) (*v1.UserPermit, error) {
	permits := make([]*RbacPermit, 0)
	prepare := `WITH a AS ( SELECT role_id FROM %s WHERE user_id = ? )
				SELECT c.* FROM ( SELECT permit_id FROM %s WHERE role_id IN ( SELECT role_id FROM a ) ) b INNER JOIN %s c ON b.permit_id = c.id ORDER BY c.sort_number DESC, c.id ASC`
	prepare = fmt.Sprintf(
		prepare,
		RbacRoleUser{}.TableName(),
		RbacRolePermit{}.TableName(),
		RbacPermit{}.TableName(),
	)
	args := []any{req.Id}
	// 超级管理员拥有所有权限
	if req.Category == 0 {
		prepare = "SELECT * FROM %s ORDER BY sort_number DESC, id ASC"
		prepare = fmt.Sprintf(prepare, RbacPermit{}.TableName())
		args = make([]any, 0)
	}
	err := s.db.DB().
		// WithContext(ctx).
		Raw(prepare, args...).
		Find(&permits).Error
	if err != nil {
		return nil, err
	}
	length := len(permits)
	all := make([]*v1.UserPermitItem, 0, length)
	top := make([]*v1.UserPermitItem, 0, 8)
	set := make(map[int64]*v1.UserPermitItem, length)
	for _, v := range permits {
		tmp := &v1.UserPermitItem{
			Id:            v.Id,
			Pid:           v.Pid,
			Category:      v.Category,
			Name:          v.Name,
			Icon:          v.Icon,
			Method:        v.Method,
			Path:          v.Path,
			Note:          v.Note,
			SortNumber:    v.SortNumber,
			State:         v.State,
			ComponentPath: v.ComponentPath,
			ComponentName: v.ComponentName,
			CreatedAt:     v.CreatedAt,
			Children:      make([]*v1.UserPermitItem, 0),
			Visible:       v.Visible,
			Redirect:      v.Redirect,
			PathUrl:       v.PathUrl,
			ActiveMenu:    v.ActiveMenu,
		}
		if _, ok := set[v.Id]; ok {
			continue
		}
		set[v.Id] = tmp
		if tmp.Pid == 0 {
			top = append(top, tmp)
		}
		all = append(all, tmp)
	}
	for _, v := range set {
		if v.Pid == 0 {
			continue
		}
		val, ok := set[v.Pid]
		if !ok {
			continue
		}
		val.Children = append(val.Children, v)
	}
	return &v1.UserPermit{
		Lists: top,
	}, nil
}

func (s *Account) CreateAccount(ctx context.Context, req *v1.CreateAccountRequest) (*v1.CreateAccountReply, error) {
	result := &v1.CreateAccountReply{}
	if req.Username == "" || req.Mobile == "" {
		result.StatusCode = 500
		result.Message = "缺少必填参数"
		return result, nil
	}
	first := &User{}
	get := s.db.DB().Model(first).Where("username = ? OR mobile = ?", req.GetUsername(), req.GetMobile())
	if req.Email != nil {
		get = get.Or("email = ?", req.GetEmail())
	}
	if err := get.First(first).Error; err != nil {
		if !errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, err
		}
	} else {
		if first.Mobile != nil && req.GetMobile() == *first.Mobile {
			result.StatusCode = 500
			result.Message = "手机号已被注册"
			return result, nil
		}
		if first.Username != nil && req.GetUsername() == *first.Username {
			result.StatusCode = 500
			result.Message = "用户名已被注册"
			return result, nil
		}
		if first.Email != nil && req.GetEmail() == *first.Email {
			result.StatusCode = 500
			result.Message = "邮箱已被注册"
			return result, nil
		}
		result.StatusCode = 500
		result.Message = "该用户已被注册"
		return result, nil
	}
	timestamp := time.Now().Unix()
	create := &User{}
	switch req.Category {
	case -1:
		create.Category = 0
	case 0:
		create.Category = 1
	default:
		if req.Category > 0 {
			create.Category = req.Category
		} else {
			create.Category = 1
		}
	}
	create.Username = &req.Username
	create.Mobile = &req.Mobile
	create.Email = req.Email
	if req.Password != nil && *req.Password != "" && req.PasswordSalt != nil && *req.PasswordSalt != "" {
		create.Password = *req.Password
		create.PasswordSalt = *req.PasswordSalt
	}
	create.CreatedAt = timestamp
	create.UpdatedAt = timestamp

	err := s.db.Transaction(nil, func(tx *gorm.DB) error {
		err := s.account.CreateUser(ctx, create, tx)
		if err != nil {
			return err
		}
		prepare := "INSERT INTO fx_user_message ( category, user_id, assoc_id, created_at, updated_at ) SELECT 1 AS category, %d AS user_id, id AS assoc_id, %d AS created_at, %d AS updated_at FROM fx_system_notice WHERE ( state = 9 )"
		prepare = fmt.Sprintf(prepare, create.Id, timestamp, timestamp)
		err = tx.Exec(prepare).Error
		if err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		return nil, err
	}

	result.StatusCode = 200
	result.Message = "注册成功"
	result.Id = create.Id
	return result, nil
}

func (s *Account) GetAccountInfo(ctx context.Context, req *v1.GetAccountInfoRequest) (*v1.GetAccountInfoReply, error) {
	user, err := s.ServerGetUser(ctx, &v1.FxUser{
		Id: req.UserId,
	})
	if err != nil {
		return nil, err
	}
	result := &v1.GetAccountInfoReply{
		StatusCode: 200,
		Message:    "success",
		Data: &v1.GetAccountInfoReply_Reply{
			Id:             user.Id,
			Mobile:         user.Mobile,
			Username:       user.Username,
			Email:          user.Email,
			Integral:       user.Integral,
			RealName:       user.RealName,
			IdCardNo:       user.IdCardNo,
			BankNo:         user.BankNo,
			BankOpen:       user.BankOpen,
			Note:           user.Note,
			IsInternal:     user.IsInternal,
			State:          user.State,
			StateReason:    user.StateReason,
			CreatedAt:      user.CreatedAt,
			ReturnNormalAt: user.ReturnNormalAt,
			IsVerifier:     user.IsVerifier,
		},
	}
	return result, nil
}

func (s *Account) UpdateAccountPassword(ctx context.Context, req *v1.UpdateAccountPasswordRequest) (*v1.UpdateAccountPasswordReply, error) {
	user, err := s.account.ServerGetUser(ctx, &v1.FxUser{
		Id: *req.UserId,
	})
	if err != nil {
		return nil, err
	}
	result := &v1.UpdateAccountPasswordReply{}
	if user == nil {
		result.StatusCode = 404
		result.Message = "用户不存在"
		return result, nil
	}
	if user.Mobile == nil || *user.Mobile == "" {
		result.StatusCode = 405
		result.Message = "手机号为空"
		return result, nil
	}
	_, err = s.db.UpdateById(nil, &User{}, user.Id, map[string]any{
		"password":   req.NewPassword,
		"updated_at": time.Now().Unix(),
	})
	if err != nil {
		result.StatusCode = 301
		result.Message = "密码更新失败"
		return result, nil
	}
	result.StatusCode = 200
	result.Message = "success"
	return result, nil
}

func (s *Account) CreateRbacPermit(ctx context.Context, req *v1.CreateRbacPermitRequest) (*v1.CreateRbacPermitReply, error) {
	create := &RbacPermit{
		Category:      req.Category,
		Name:          req.Name,
		Icon:          req.Icon,
		Method:        req.Method,
		Path:          req.Path,
		ComponentPath: req.ComponentPath,
		ComponentName: req.ComponentName,
		Visible:       req.Visible,
		Redirect:      req.Redirect,
		PathUrl:       req.PathUrl,
		ActiveMenu:    req.ActiveMenu,
	}
	create.Pid = req.Pid
	create.Note = req.Note
	create.SortNumber = req.SortNumber
	timestamp := time.Now().Unix()
	create.CreatedAt = timestamp
	if create.UpdatedAt != create.CreatedAt {
		create.UpdatedAt = create.CreatedAt
	}
	_, err := s.db.Insert(nil, create.TableName(), create, func(db *gorm.DB) *gorm.DB {
		return sqlContext(ctx)(db)
	})
	if err != nil {
		return nil, err
	}
	result := &v1.CreateRbacPermitReply{}
	if create.Id <= 0 {
		result.StatusCode = 501
		result.Message = "创建失败"
		return result, nil
	}
	result.StatusCode = 200
	result.Message = "success"
	result.Id = create.Id
	return result, nil
}

func (s *Account) DeleteRbacPermit(ctx context.Context, req *v1.DeleteRbacPermitRequest) (*v1.DeleteRbacPermitReply, error) {
	result := &v1.DeleteRbacPermitReply{}
	length := len(req.IdList)
	if length == 0 {
		result.StatusCode = 200
		result.Message = "success"
		return result, nil
	}
	lists := make([]int64, 0, length)
	for _, id := range req.IdList {
		lists = append(lists, id)
	}
	table := &RbacPermit{}
	// 递归查询当前待删除权限列表及其所有子集, 最终一并删除, 以达到数据完整的效果
	prepare := `
WITH RECURSIVE result AS (
	SELECT id, pid FROM fx_rbac_permit WHERE ( pid IN ( %s ) )
	UNION ALL 
	SELECT b.id, b.pid FROM fx_rbac_permit b JOIN result a ON b.pid = a.id
) 
SELECT id FROM result ORDER BY id ASC`
	ids := make([]string, 0, length)
	for _, id := range lists {
		ids = append(ids, fmt.Sprintf("%d", id))
	}
	prepare = fmt.Sprintf(prepare, strings.Join(ids, ","))
	idList := make([]int64, 0)
	err := s.db.DB().Raw(prepare).Scan(&idList).Error
	if err != nil {
		return nil, err
	}
	// 将子元素追加到待删除的列表中
	lists = append(lists, idList...)
	// 删除权限数据的同时删除角色绑定的权限数据
	err = s.db.Transaction(nil, func(tx *gorm.DB) error {
		result.Total, err = s.db.DeleteByIds(tx, table.TableName(), lists)
		if err != nil {
			return err
		}
		_, err = s.db.Delete(tx, &RbacRolePermit{}, func(db *gorm.DB) *gorm.DB {
			return sqlIn("permit_id", lists)(sqlContext(ctx)(db))
		})
		if err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	result.StatusCode = 200
	result.Message = "success"
	return result, nil
}

func (s *Account) UpdateRbacPermit(ctx context.Context, req *v1.UpdateRbacPermitRequest) (*v1.UpdateRbacPermitReply, error) {
	table := &RbacPermit{}
	length := len(req.Lists)
	result := &v1.UpdateRbacPermitReply{}
	updates := make(map[int64]map[string]any, length)
	timestamp := time.Now().Unix()
	for _, v := range req.Lists {
		if v.Id <= 0 {
			continue
		}
		update := make(map[string]any, 8)
		if v.Name != nil {
			update["name"] = v.Name
		}
		if v.Icon != nil {
			update["icon"] = v.Icon
		}
		if v.Method != nil {
			update["method"] = v.Method
		}
		if v.Path != nil {
			update["path"] = v.Path
		}
		if v.Note != nil {
			update["note"] = v.Note
		}
		if v.SortNumber != nil {
			update["sort_number"] = v.SortNumber
		}
		if v.State != nil {
			update["state"] = v.State
		}
		if v.ComponentPath != nil {
			update["component_path"] = v.ComponentPath
		}
		if v.ComponentName != nil {
			update["component_name"] = v.ComponentName
		}
		if v.Visible != nil {
			update["visible"] = v.Visible
		}
		if v.Redirect != nil {
			update["redirect"] = v.Redirect
		}
		if v.PathUrl != nil {
			update["path_url"] = v.PathUrl
		}
		if len(update) > 0 {
			update["updated_at"] = timestamp
			updates[v.Id] = update
		}
	}
	err := s.db.Transaction(nil, func(tx *gorm.DB) error {
		// 特别提示: 数据量不大允许可以考虑使用循环更新
		for id, update := range updates {
			rows, err := s.db.Update(tx, table, func(db *gorm.DB) *gorm.DB {
				return sqlEqual("id", id)(sqlContext(ctx)(db))
			}, update)
			if err != nil {
				return err
			}
			result.Total += rows
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	result.StatusCode = 200
	result.Message = "success"
	return result, nil
}

func (s *Account) ListsRbacPermit(ctx context.Context, req *v1.ListsRbacPermitRequest) (*v1.ListsRbacPermitReply, error) {
	table := &RbacPermit{}
	result := &v1.ListsRbacPermitReply{}
	get := s.db.DB().Table(table.TableName())
	if req.Category != nil {
		get = get.Where("category = ?", *req.Category)
	}
	if req.Method != nil {
		get = get.Where("method = ?", *req.Method)
	}
	if req.Path != nil {
		get = get.Where("path = ?", *req.Path)
	}
	if req.Note != nil {
		get = get.Where("note = ?", *req.Note)
	}
	if req.State != nil {
		get = get.Where("state = ?", *req.State)
	}
	if req.ComponentPath != nil {
		get = get.Where("component_path = ?", *req.ComponentPath)
	}
	if req.ComponentName != nil {
		get = get.Where("component_name = ?", *req.ComponentName)
	}
	if req.Keyword != nil && *req.Keyword != "" {
		keyword := fmt.Sprintf("%%%s%%", *req.Keyword)
		get = get.Where("( name LIKE ? OR path LIKE ? OR component_path LIKE ? OR component_name LIKE ? OR note LIKE ? )", keyword, keyword, keyword, keyword, keyword)
	}
	lists := make([]*RbacPermit, 0)
	err := get.Order("sort_number DESC").Order("id").Find(&lists).Error
	if err != nil {
		return nil, err
	}
	outputMap := make(map[int64]*v1.ListsRbacPermitReply_Item, len(lists))
	output := make([]*v1.ListsRbacPermitReply_Item, 0, 8)
	outputAll := make([]*v1.ListsRbacPermitReply_Item, 0, len(lists))
	for _, v := range lists {
		tmp := &v1.ListsRbacPermitReply_Item{
			Id:            v.Id,
			Pid:           v.Pid,
			Category:      v.Category,
			Name:          v.Name,
			Icon:          v.Icon,
			Method:        v.Method,
			Path:          v.Path,
			Note:          v.Note,
			SortNumber:    v.SortNumber,
			State:         v.State,
			ComponentPath: v.ComponentPath,
			ComponentName: v.ComponentName,
			CreatedAt:     v.CreatedAt,
			Children:      make([]*v1.ListsRbacPermitReply_Item, 0),
			Visible:       v.Visible,
			Redirect:      v.Redirect,
			PathUrl:       v.PathUrl,
			ActiveMenu:    v.ActiveMenu,
		}
		outputMap[v.Id] = tmp
		if tmp.Pid == 0 {
			output = append(output, tmp)
		}
		outputAll = append(outputAll, tmp)
	}
	for _, v := range outputMap {
		if v.Pid == 0 {
			continue
		}
		val, ok := outputMap[v.Pid]
		if !ok {
			continue
		}
		val.Children = append(val.Children, v)
	}
	result.StatusCode = 200
	result.Message = "success"
	result.Data = output
	return result, nil
}

func (s *Account) CreateRbacRole(ctx context.Context, req *v1.CreateRbacRoleRequest) (*v1.CreateRbacRoleReply, error) {
	create := &RbacRole{
		Name:       req.Name,
		Identifier: req.Identifier,
		State:      req.State,
	}
	create.Note = req.Note
	create.SortNumber = req.SortNumber
	timestamp := time.Now().Unix()
	create.CreatedAt = timestamp
	if create.UpdatedAt != create.CreatedAt {
		create.UpdatedAt = create.CreatedAt
	}
	tx := s.db.DB().Begin()
	defer tx.Rollback()
	had := &RbacRole{}
	err := tx.Table(create.TableName()).Where("identifier = ?", req.Identifier).Select("id").First(had).Error
	result := &v1.CreateRbacRoleReply{}
	if err != nil {
		if !errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, err
		}
	} else {
		result.StatusCode = 500
		result.Message = "该角色已存在"
		return result, nil
	}
	_, err = s.db.Insert(tx, create.TableName(), create, func(db *gorm.DB) *gorm.DB {
		return sqlContext(ctx)(db)
	})
	if err != nil {
		return nil, err
	}
	if create.Id <= 0 {
		result.StatusCode = 501
		result.Message = "创建失败"
		return result, nil
	}
	tx.Commit()
	result.StatusCode = 200
	result.Message = "success"
	result.Id = create.Id
	return result, nil
}

func (s *Account) DeleteRbacRole(ctx context.Context, req *v1.DeleteRbacRoleRequest) (*v1.DeleteRbacRoleReply, error) {
	table := &RbacRole{}
	lists := make([]int64, 0, len(req.IdList))
	for _, id := range req.IdList {
		lists = append(lists, id)
	}
	err := s.db.Transaction(nil, func(tx *gorm.DB) error {
		_, err := s.db.Delete(tx, table.TableName(), func(db *gorm.DB) *gorm.DB {
			return sqlIn("id", req.IdList)(sqlContext(ctx)(db))
		})
		if err != nil {
			return err
		}
		_, err = s.db.Delete(tx, &RbacRolePermit{}, func(db *gorm.DB) *gorm.DB {
			return sqlIn("role_id", req.IdList)(sqlContext(ctx)(db))
		})
		if err != nil {
			return err
		}
		_, err = s.db.Delete(tx, &RbacRoleUser{}, func(db *gorm.DB) *gorm.DB {
			return sqlIn("role_id", req.IdList)(sqlContext(ctx)(db))
		})
		if err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	result := &v1.DeleteRbacRoleReply{}
	result.StatusCode = 200
	result.Message = "success"
	result.Total = int64(len(req.IdList))
	return result, nil
}

func (s *Account) UpdateRbacRole(ctx context.Context, req *v1.UpdateRbacRoleRequest) (*v1.UpdateRbacRoleReply, error) {
	table := &RbacRole{}
	length := len(req.Lists)
	result := &v1.UpdateRbacRoleReply{}
	updates := make(map[int64]map[string]any, length)
	permits := make(map[int64][]*RbacRolePermit, length)
	users := make(map[int64][]*RbacRoleUser, length)
	timestamp := time.Now().Unix()
	for _, v := range req.Lists {
		if v.Id <= 0 {
			continue
		}
		update := make(map[string]any, 8)
		if v.Name != nil {
			update["name"] = v.Name
		}
		if v.Note != nil {
			update["note"] = v.Note
		}
		if v.SortNumber != nil {
			update["sort_number"] = v.SortNumber
		}
		if v.State != nil {
			update["state"] = v.GetState()
		}
		if len(update) == 0 {
			continue
		}
		update["updated_at"] = timestamp
		updates[v.Id] = update

		permit := make([]*RbacRolePermit, 0, len(v.PermitIdLists))
		for _, w := range v.PermitIdLists {
			tmp := &RbacRolePermit{
				RoleId:   v.Id,
				PermitId: w,
			}
			tmp.CreatedAt = timestamp
			tmp.UpdatedAt = timestamp
			permit = append(permit, tmp)
		}
		permits[v.Id] = permit

		user := make([]*RbacRoleUser, 0, len(v.UserIdLists))
		for _, w := range v.UserIdLists {
			tmp := &RbacRoleUser{
				RoleId: v.Id,
			}
			tmp.UserId = w
			tmp.CreatedAt = timestamp
			tmp.UpdatedAt = timestamp
			user = append(user, tmp)
		}
		users[v.Id] = user
	}

	err := s.db.Transaction(nil, func(tx *gorm.DB) error {
		// 特别提示: 数据量不大允许可以考虑使用循环更新
		for id, update := range updates {
			rows, err := s.db.Update(tx, table, func(db *gorm.DB) *gorm.DB {
				return sqlEqual("id", id)(sqlContext(ctx)(db))
			}, update)
			if err != nil {
				return err
			}
			result.Total += rows
			// 先删除现有数据,再插入设置的权限列表和角色列表数据
			permit, user := &RbacRolePermit{}, &RbacRoleUser{}
			_, err = s.db.Delete(tx, permit.TableName(), func(db *gorm.DB) *gorm.DB {
				return sqlEqual("role_id", id)(sqlContext(ctx)(db))
			})
			if err != nil {
				return err
			}
			_, err = s.db.Delete(tx, user.TableName(), func(db *gorm.DB) *gorm.DB {
				return sqlEqual("role_id", id)(sqlContext(ctx)(db))
			})
			if err != nil {
				return err
			}
			if value := permits[id]; len(value) > 0 {
				_, err = s.db.Insert(tx, permit.TableName(), value, nil)
				if err != nil {
					return err
				}
			}
			if value := users[id]; len(value) > 0 {
				_, err = s.db.Insert(tx, user.TableName(), value, nil)
				if err != nil {
					return err
				}
			}
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	result.StatusCode = 200
	result.Message = "success"
	return result, nil
}

func (s *Account) ListsRbacRole(ctx context.Context, req *v1.ListsRbacRoleRequest) (*v1.ListsRbacRoleReply, error) {
	table := &RbacRole{}
	result := &v1.ListsRbacRoleReply{}
	get := s.db.DB().Table(table.TableName())
	if req.Identifier != nil {
		get.Where("identifier = ?", *req.Identifier)
	}
	if req.State != nil {
		get.Where("state = ?", req.GetState())
	}
	if req.Keyword != nil && *req.Keyword != "" {
		keyword := fmt.Sprintf("%%%s%%", *req.Keyword)
		get.Where("( name LIKE ? OR note LIKE ? )", keyword, keyword)
	}
	lists := make([]*RbacRole, 0)
	err := get.Order("sort_number DESC").Order("id").Find(&lists).Error
	if err != nil {
		return nil, err
	}
	output := make([]*v1.ListsRbacRoleReply_Item, 0, 8)
	roleIdList := make([]int64, 0, len(lists))
	for _, v := range lists {
		tmp := &v1.ListsRbacRoleReply_Item{
			Id:         v.Id,
			Name:       v.Name,
			Identifier: v.Identifier,
			Note:       v.Note,
			SortNumber: v.SortNumber,
			State:      v.State,
			CreatedAt:  v.CreatedAt,
		}
		output = append(output, tmp)
		roleIdList = append(roleIdList, v.Id)
	}
	{
		permits := make([]*RbacPermit, 0)
		prepare := "SELECT a.role_id AS created_at, b.id, b.pid, b.category, b.name, b.icon, b.method, b.path, b.note FROM %s a INNER JOIN %s b ON a.permit_id = b.id WHERE a.role_id IN (?) ORDER BY b.sort_number DESC, b.id ASC"
		prepare = fmt.Sprintf(
			prepare,
			RbacRolePermit{}.TableName(),
			RbacPermit{}.TableName(),
		)
		err = s.db.DB().Raw(prepare, roleIdList).Find(&permits).Error
		if err != nil {
			return nil, err
		}
		rolePermits := make(map[int64][]*v1.ListsRbacRoleReply_Item_Permit, len(roleIdList))
		for _, v := range permits {
			if _, ok := rolePermits[v.CreatedAt]; !ok {
				rolePermits[v.CreatedAt] = make([]*v1.ListsRbacRoleReply_Item_Permit, 0, 1)
			}
			tmp := &v1.ListsRbacRoleReply_Item_Permit{
				Id:       v.Id,
				Pid:      v.Pid,
				Category: v.Category,
				Name:     v.Name,
				Icon:     v.Icon,
				Method:   v.Method,
				Path:     v.Path,
				Note:     v.Note,
			}
			rolePermits[v.CreatedAt] = append(rolePermits[v.CreatedAt], tmp)
		}
		users := make([]*User, 0)
		prepare = "SELECT a.role_id AS created_at, b.id, b.category, b.mobile, b.username, b.email, b.real_name, b.note FROM %s a INNER JOIN %s b ON a.user_id = b.id WHERE a.role_id IN (?) AND b.deleted_at = 0 ORDER BY b.id ASC"
		prepare = fmt.Sprintf(
			prepare,
			RbacRoleUser{}.TableName(),
			User{}.TableName(),
		)
		err = s.db.DB().Raw(prepare, roleIdList).Find(&users).Error
		if err != nil {
			return nil, err
		}
		roleUsers := make(map[int64][]*v1.ListsRbacRoleReply_Item_User, len(roleIdList))
		for _, v := range users {
			roleId := v.CreatedAt
			if _, ok := roleUsers[roleId]; !ok {
				roleUsers[roleId] = make([]*v1.ListsRbacRoleReply_Item_User, 0, 1)
			}
			tmp := &v1.ListsRbacRoleReply_Item_User{
				Id:       v.Id,
				Category: v.Category,
				Mobile:   v.Mobile,
				Username: v.Username,
				Email:    v.Email,
				RealName: v.RealName,
				Note:     v.Note,
			}
			roleUsers[roleId] = append(roleUsers[roleId], tmp)
		}
		for _, v := range output {
			if w, ok := rolePermits[v.Id]; ok {
				v.PermitIdLists = w
			} else {
				v.PermitIdLists = make([]*v1.ListsRbacRoleReply_Item_Permit, 0)
			}
			if w, ok := roleUsers[v.Id]; ok {
				v.UserIdLists = w
			} else {
				v.UserIdLists = make([]*v1.ListsRbacRoleReply_Item_User, 0)
			}
		}
	}
	result.StatusCode = 200
	result.Message = "success"
	result.Data = output
	return result, nil
}

func (s *Account) RbacUserList(ctx context.Context, req *v1.RbacUserListRequest) (*v1.RbacUserListReply, error) {
	result := &v1.RbacUserListReply{}
	lists := make([]*User, 0)
	err := s.db.DB().Model(&User{}).Where("deleted_at = 0").Select("id, mobile, username, real_name").Order("id").Find(&lists).Error
	if err != nil {
		return nil, err
	}
	result.Lists = make([]*v1.RbacUserListReply_Item, 0, len(lists))
	for _, v := range lists {
		result.Lists = append(result.Lists, &v1.RbacUserListReply_Item{
			Id:       v.Id,
			Mobile:   v.Mobile,
			Username: v.Username,
			RealName: v.RealName,
		})
	}
	return result, nil
}

func (s *Account) RbacPermitList(ctx context.Context, req *v1.RbacPermitListRequest) (*v1.RbacPermitListReply, error) {
	result := &v1.RbacPermitListReply{}
	lists := make([]*RbacPermit, 0)
	err := s.db.DB().
		Model(&RbacPermit{}).
		Select("id, pid, category, name, icon").
		Order("sort_number DESC").
		Order("id").
		Find(&lists).
		Error
	if err != nil {
		return nil, err
	}
	result.Lists = make([]*v1.RbacPermitListReply_Item, 0, len(lists))
	for _, v := range lists {
		result.Lists = append(result.Lists, &v1.RbacPermitListReply_Item{
			Id:       v.Id,
			Pid:      v.Pid,
			Category: v.Category,
			Name:     v.Name,
			Icon:     v.Icon,
		})
	}
	{
		top := make([]*v1.RbacPermitListReply_Item, 0, 1<<3)
		set := make(map[int64]*v1.RbacPermitListReply_Item, 1<<5)
		for _, v := range result.Lists {
			if v.Pid == 0 {
				top = append(top, v)
			}
			set[v.Id] = v
		}
		for _, v := range result.Lists {
			w, ok := set[v.Pid]
			if ok {
				w.Children = append(w.Children, v)
			}
		}
		result.Lists = top
	}
	return result, nil
}

func (s *Account) ListsUser(ctx context.Context, req *v1.ListsUserRequest) (*v1.ListsUserReply, error) {
	result := &v1.ListsUserReply{}
	get := s.db.DB().Model(&User{})
	if req.IsInternal != nil {
		get = get.Where("is_internal = ?", req.GetIsInternal())
	}
	if req.State != nil {
		get = get.Where("state = ?", req.GetState())
	}
	if req.Keyword != nil && *req.Keyword != "" {
		keyword := fmt.Sprintf("%%%s%%", *req.Keyword)
		get = get.Where("( username LIKE ? OR mobile LIKE ? OR real_name LIKE ? )", keyword, keyword, keyword)
	}
	get = get.Where("deleted_at = 0")
	err := get.Count(&result.Total).Error
	if err != nil {
		return nil, err
	}
	page := getPage(req.GetPage())
	limit := getLimit(req.GetLimit())
	offset := getOffset(page, limit)
	get.Order("id DESC").Limit(limit).Offset(offset)
	lists := make([]*User, 0)
	err = get.Find(&lists).Error
	if err != nil {
		return nil, err
	}
	result.Data = make([]*v1.ListsUserReply_Item, 0, len(lists))
	userIds := make([]int64, 0, 1<<8)
	for _, v := range lists {
		userIds = append(userIds, v.Id)
		result.Data = append(result.Data, &v1.ListsUserReply_Item{
			Id:             v.Id,
			Category:       v.Category,
			Mobile:         v.Mobile,
			Username:       v.Username,
			Email:          v.Email,
			Integral:       v.Integral,
			RealName:       v.RealName,
			IdCardNo:       v.IdCardNo,
			BankNo:         v.BankNo,
			BankOpen:       v.BankOpen,
			Note:           v.Note,
			IsInternal:     int32(v.IsInternal),
			State:          v.State,
			StateReason:    v.StateReason,
			ReturnNormalAt: v.ReturnNormalAt,
			CreatedAt:      v.CreatedAt,
			UpdatedAt:      v.UpdatedAt,
		})
	}
	if len(userIds) > 0 {
		sources := make([]*UserApproveSources, 0)
		err = s.db.DB().Model(&UserApproveSources{}).Where("user_id IN (?)", userIds).Find(&sources).Error
		if err != nil {
			return nil, err
		}
		sourcesMap := make(map[int64][]int64, 1<<3)
		for _, v := range sources {
			if _, ok := sourcesMap[v.UserId]; !ok {
				sourcesMap[v.UserId] = make([]int64, 0, 1)
			}
			sourcesMap[v.UserId] = append(sourcesMap[v.UserId], v.ApproveSourcesId)
		}
		for _, v := range result.Data {
			if w, ok := sourcesMap[v.Id]; ok {
				v.ApproveSources = w
			} else {
				v.ApproveSources = make([]int64, 0)
			}
		}
	}
	result.StatusCode = 200
	result.Message = "success"
	return result, nil
}

func (s *Account) UpdateUser(ctx context.Context, req *v1.UpdateUserRequest) (*v1.UpdateUserReply, error) {
	result := &v1.UpdateUserReply{}
	user := &User{}
	err := s.db.DB().Model(user).Where("id = ?", req.Id).Where("deleted_at = 0").First(user).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			result.StatusCode = 405
			result.Message = "user not found"
			return result, nil
		}
		return nil, err
	}
	timestamp := time.Now().Unix()
	update := make(map[string]any)
	if req.Category != nil {
		update["category"] = *req.Category
	}
	if req.Mobile != nil {
		update["mobile"] = *req.Mobile
	}
	if req.Username != nil {
		update["username"] = *req.Username
	}
	if req.Email != nil {
		update["email"] = *req.Email
	}
	if req.RealName != nil {
		update["real_name"] = *req.RealName
	}
	if req.IdCardNo != nil {
		update["id_card_no"] = *req.IdCardNo
	}
	if req.BankNo != nil {
		update["bank_no"] = *req.BankNo
	}
	if req.BankOpen != nil {
		update["bank_open"] = *req.BankOpen
	}
	if req.Note != nil {
		update["note"] = *req.Note
	}
	if req.IsInternal != nil {
		update["is_internal"] = *req.IsInternal
	}
	if req.State != nil && *req.State != user.State {
		update["state"] = *req.State
		if req.StateReason != nil {
			update["state_reason"] = *req.StateReason
		}
	}
	if req.ReturnNormalAt != nil && *req.ReturnNormalAt > timestamp {
		update["return_normal_at"] = *req.ReturnNormalAt
	}
	if req.Password != nil {
		update["password"] = *req.Password
	}
	err = s.db.DB().Transaction(func(tx *gorm.DB) error {
		if req.ApproveSources != nil {
			sources := strings.Split(*req.ApproveSources, ",")
			lists := make([]*UserApproveSources, 0, len(sources))
			for _, v := range sources {
				i64, err := strconv.ParseInt(v, 10, 64)
				if err != nil {
					return err
				}
				if i64 <= 0 {
					continue
				}
				tmp := &UserApproveSources{
					ApproveSourcesId: i64,
				}
				tmp.UserId = user.Id
				tmp.CreatedAt = timestamp
				tmp.UpdatedAt = timestamp
				lists = append(lists, tmp)
			}
			uas := &UserApproveSources{}
			if err = tx.Table(uas.TableName()).Where("user_id = ?", user.Id).Unscoped().Delete(uas).Error; err != nil {
				return err
			}
			if len(lists) > 0 {
				if err = tx.Table(uas.TableName()).Create(lists).Error; err != nil {
					return err
				}
			}
		}
		if len(update) > 0 {
			update["updated_at"] = timestamp
			err = tx.Table(user.TableName()).Where("id = ?", user.Id).Updates(update).Error
			if err != nil {
				return err
			}
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	result.StatusCode = 200
	result.Message = "success"
	return result, nil
}

func (s *Account) DeleteUser(ctx context.Context, req *v1.DeleteUserRequest) (*v1.DeleteUserReply, error) {
	result := &v1.DeleteUserReply{}
	user := &User{}
	timestamp := time.Now().Unix()
	lists := make([]int64, 0, 1)
	if req.Id > 0 {
		lists = append(lists, req.Id)
	}
	if len(req.IdList) > 0 {
		lists = append(lists, req.IdList...)
	}
	err := s.db.DB().Model(user).Where("id IN ? AND deleted_at = 0", lists).Updates(map[string]any{
		"updated_at": timestamp,
		"deleted_at": timestamp,
	}).Error
	if err != nil {
		return nil, err
	}
	result.StatusCode = 200
	result.Message = "success"
	return result, nil
}

func (s *Account) CreateSystemNotice(ctx context.Context, req *v1.CreateSystemNoticeRequest) (*v1.Response, error) {
	reply := &v1.Response{}
	create := &SystemNotice{
		Category:      req.Category,
		Title:         req.Title,
		Content:       req.Content,
		State:         1,
		IsTop:         int8(req.GetIsTop()),
		PublishAt:     0,
		PublishUserId: 0,
	}
	timestamp := time.Now().Unix()
	create.SortNumber = req.GetSortNumber()
	create.CreatedAt = timestamp
	create.UpdatedAt = timestamp
	_, err := s.db.Insert(nil, &SystemNotice{}, create, func(db *gorm.DB) *gorm.DB {
		return sqlContext(ctx)(db)
	})
	if err != nil {
		return nil, err
	}
	reply.StatusCode = 200
	reply.Message = "success"
	return reply, nil
}

func (s *Account) DeleteSystemNotice(ctx context.Context, req *v1.DeleteSystemNoticeRequest) (*v1.Response, error) {
	reply := &v1.Response{}
	timestamp := time.Now().Unix()
	_, err := s.db.Update(nil, &SystemNotice{}, func(db *gorm.DB) *gorm.DB {
		return sqlContext(ctx)(db).Where("id IN (?)", req.GetIdList())
	}, map[string]any{
		"state":      17,
		"updated_at": timestamp,
	})
	if err != nil {
		return nil, err
	}
	reply.StatusCode = 200
	reply.Message = "success"
	return reply, nil
}

func (s *Account) UpdateSystemNotice(ctx context.Context, req *v1.UpdateSystemNoticeRequest) (*v1.Response, error) {
	reply := &v1.Response{}
	notice := &SystemNotice{}
	err := s.db.DB().Model(notice).Where("id = ?", req.Id).Select("id", "state").First(&notice).Error
	if err != nil {
		return nil, err
	}
	timestamp := time.Now().Unix()
	update := make(map[string]any)
	if req.Title != nil {
		update["title"] = req.GetTitle()
	}
	if req.Content != nil {
		update["content"] = req.GetContent()
	}
	if req.Category != nil {
		update["category"] = req.GetCategory()
	}
	if req.State != nil {
		state := req.GetState()
		update["state"] = state
		if notice.State == 1 && state == 9 {
			update["publish_at"] = timestamp
			update["publish_user_id"] = req.GetUserId()
		}
	}
	if req.IsTop != nil {
		update["is_top"] = req.GetIsTop()
	}
	if req.SortNumber != nil {
		update["sort_number"] = req.GetSortNumber()
	}
	if len(update) == 0 {
		reply.StatusCode = 200
		reply.Message = "success"
		return reply, nil
	}
	update["updated_at"] = timestamp
	err = s.db.Transaction(nil, func(tx *gorm.DB) error {
		_, err = s.db.Update(tx, &SystemNotice{}, func(db *gorm.DB) *gorm.DB {
			return sqlContext(ctx)(db).Where("id = ?", req.Id)
		}, update)
		if err != nil {
			return err
		}
		// 用户阅读消息数据
		if req.State != nil && *req.State == 9 && notice.State == 1 {
			lists := make([]*UserMessage, 0, 1)
			err = tx.Model(&UserMessage{}).
				Where("category = 1 AND assoc_id = ?", req.Id).
				Select("id").
				Limit(1).
				Find(&lists).
				Error
			if err != nil {
				return err
			}
			if len(lists) == 0 {
				prepare := "INSERT INTO fx_user_message ( category, user_id, assoc_id, created_at, updated_at ) SELECT 1 AS category, id AS user_id, %d AS assoc_id, %d AS created_at, %d AS updated_at FROM fx_user"
				prepare = fmt.Sprintf(prepare, req.Id, timestamp, timestamp)
				err = tx.Exec(prepare).Error
				if err != nil {
					return err
				}
			}
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	reply.StatusCode = 200
	reply.Message = "success"
	return reply, nil
}

func (s *Account) SelectSystemNotice(ctx context.Context, req *v1.SelectSystemNoticeRequest) (*v1.SelectSystemNoticeReply, error) {
	result := &v1.SelectSystemNoticeReply{}
	get := s.db.DB().Model(&SystemNotice{})
	if req.PublishStartAt != nil && req.PublishEndAt != nil && *req.PublishStartAt <= *req.PublishEndAt && *req.PublishStartAt > 0 {
		get = get.Where("publish_at BETWEEN ? AND ?", *req.PublishStartAt, *req.PublishEndAt)
	}
	if req.Keyword != nil && *req.Keyword != "" {
		keyword := fmt.Sprintf("%%%s%%", *req.Keyword)
		get = get.Where("( title LIKE ? )", keyword)
	}
	get = get.Where("state <> ?", 17)
	err := get.Count(&result.Total).Error
	if err != nil {
		return nil, err
	}
	page := getPage(req.GetPage())
	limit := getLimit(req.GetLimit())
	offset := getOffset(page, limit)
	get.Order("is_top DESC").Order("sort_number DESC").Order("id DESC").Limit(limit).Offset(offset)
	lists := make([]*SystemNotice, 0)
	err = get.Find(&lists).Error
	if err != nil {
		return nil, err
	}
	result.Data = make([]*v1.SelectSystemNoticeReply_Item, 0, len(lists))
	for _, v := range lists {
		result.Data = append(result.Data, &v1.SelectSystemNoticeReply_Item{
			Id:            v.Id,
			Category:      v.Category,
			Title:         v.Title,
			Content:       &v.Content,
			State:         v.State,
			IsTop:         int32(v.IsTop),
			SortNumber:    v.SortNumber,
			PublishAt:     v.PublishAt,
			PublishUserId: v.PublishUserId,
			CreatedAt:     v.CreatedAt,
		})
	}
	result.StatusCode = 200
	result.Message = "success"
	return result, nil
}

func (s *Account) UserMessageUnread(ctx context.Context, req *v1.UserMessageUnreadRequest) (*v1.UserMessageUnreadReply, error) {
	result := &v1.UserMessageUnreadReply{}
	if req.UserId == nil {
		result.StatusCode = 200
		result.Message = "success"
		return result, nil
	}
	count, err := s.db.Table(&UserMessage{}).Scopes(func(db *gorm.DB) *gorm.DB {
		return sqlContext(ctx)(db).Where("user_id = ? AND user_read_at = ?", req.GetUserId(), 0)
	}).Count()
	if err != nil {
		return nil, err
	}
	result.StatusCode = 200
	result.Message = "success"
	result.Total = count
	return result, nil
}

type userMessageItem struct {
	// id
	Id int64 `gorm:"column:id"`
	// 消息分类 1:系统公告
	Category int32 `gorm:"column:category"`
	// 关联id
	AssocId int64 `gorm:"column:assoc_id"`
	// 用户阅读时间
	UserReadAt int64 `gorm:"column:user_read_at"`
	// 标题
	Title string `gorm:"column:title"`
	// 内容
	Content *string `gorm:"column:content"`
	// 发布时间
	PublishAt int64 `gorm:"column:publish_at"`
	// 发布者
	PublishUserId int64 `gorm:"column:publish_user_id"`
	// 发布者
	PublishUsername *string `gorm:"column:publish_username"`
	// 发布者姓名
	PublishRealName *string `gorm:"column:publish_real_name"`
}

func (s *Account) UserMessageLists(ctx context.Context, req *v1.UserMessageListsRequest) (*v1.UserMessageListsReply, error) {
	result := &v1.UserMessageListsReply{}
	noticeTable := fmt.Sprintf("( SELECT * FROM fx_user_message WHERE ( user_id = %d AND category = 1 ) ) a", req.GetUserId())
	notice := s.db.DB().Table(noticeTable).Joins("INNER JOIN system_notice b ON a.assoc_id = b.id")
	notice = notice.Joins("LEFT JOIN user c ON b.publish_user_id = c.id")
	if req.PublishStartAt != nil && req.PublishEndAt != nil && *req.PublishStartAt <= *req.PublishEndAt && *req.PublishStartAt > 0 {
		notice = notice.Where("b.publish_at BETWEEN ? AND ?", *req.PublishStartAt, *req.PublishEndAt)
	}
	if req.Read != nil {
		switch req.GetRead() {
		case 1:
			notice = notice.Where("a.user_read_at > 0")
		case 2:
			notice = notice.Where("a.user_read_at = 0")
		default:
		}
	}
	if keyword := req.GetKeyword(); keyword != "" {
		keyword = fmt.Sprintf("%%%s%%", keyword)
		notice = notice.Where("b.title LIKE ?", keyword)
	}
	notice = notice
	err := notice.Count(&result.Total).Error
	if err != nil {
		return nil, err
	}
	notice = notice.Select("a.id, a.category, a.assoc_id, a.user_read_at, b.title, b.content, b.publish_at, b.publish_user_id, c.username AS publish_username, c.real_name AS publish_real_name")
	page := getPage(req.GetPage())
	limit := getLimit(req.GetLimit())
	offset := getOffset(page, limit)
	notice.Order("a.id DESC").Limit(limit).Offset(offset)
	lists := make([]*userMessageItem, 0)
	if err = notice.Find(&lists).Error; err != nil {
		return nil, err
	}
	items := make([]*v1.UserMessageListsReply_Item, len(lists))
	for i, v := range lists {
		items[i] = &v1.UserMessageListsReply_Item{
			Id:              v.Id,
			Category:        v.Category,
			AssocId:         v.AssocId,
			UserReadAt:      v.UserReadAt,
			Title:           v.Title,
			Content:         v.Content,
			PublishAt:       v.PublishAt,
			PublishUserId:   v.PublishUserId,
			PublishUsername: v.PublishUsername,
			PublishRealName: v.PublishRealName,
		}
	}
	result.StatusCode = 200
	result.Message = "success"
	result.Data = items
	return result, nil
}

func (s *Account) UserMessageRead(ctx context.Context, req *v1.UserMessageReadRequest) (*v1.UserMessageReadReply, error) {
	result := &v1.UserMessageReadReply{}
	timestamp := time.Now().Unix()
	err := s.db.DB().
		Model(&UserMessage{}).
		Where("id = ? AND user_id = ? AND user_read_at = 0", req.GetMessageId(), req.GetUserId()).
		Updates(map[string]any{
			"user_read_at": timestamp,
			"updated_at":   timestamp,
		}).Error
	if err != nil {
		return nil, err
	}
	result.StatusCode = 200
	result.Message = "success"
	return result, nil
}

func (s *Account) ListsReader(ctx context.Context, req *v1.ListsReaderRequest) (*v1.ListsReaderReply, error) {
	result := &v1.ListsReaderReply{}
	get := s.db.DB().Model(&User{})
	get.Where("EXISTS ( SELECT 1 FROM fx_rbac_role_user WHERE ( user.id = rbac_role_user.user_id AND role_id IN ( SELECT id FROM fx_rbac_role WHERE ( identifier = 'reader' ) LIMIT 1 ) ) )")
	if req.Keyword != nil && *req.Keyword != "" {
		keyword := fmt.Sprintf("%%%s%%", *req.Keyword)
		get = get.Where("( username LIKE ? OR mobile LIKE ? OR real_name LIKE ? )", keyword, keyword, keyword)
	}
	err := get.Count(&result.Total).Error
	if err != nil {
		return nil, err
	}
	page := getPage(req.GetPage())
	limit := getLimit(req.GetLimit())
	offset := getOffset(page, limit)
	get.Order("id DESC").Limit(limit).Offset(offset)
	lists := make([]*User, 0)
	err = get.Find(&lists).Error
	if err != nil {
		return nil, err
	}
	result.Data = make([]*v1.ListsReaderReply_Item, 0, len(lists))
	for _, v := range lists {
		result.Data = append(result.Data, &v1.ListsReaderReply_Item{
			Id:             v.Id,
			Category:       v.Category,
			Mobile:         v.Mobile,
			Username:       v.Username,
			Email:          v.Email,
			Integral:       v.Integral,
			RealName:       v.RealName,
			IdCardNo:       v.IdCardNo,
			BankNo:         v.BankNo,
			BankOpen:       v.BankOpen,
			Note:           v.Note,
			IsInternal:     int32(v.IsInternal),
			State:          v.State,
			StateReason:    v.StateReason,
			ReturnNormalAt: v.ReturnNormalAt,
			CreatedAt:      v.CreatedAt,
			UpdatedAt:      v.UpdatedAt,
		})
	}
	result.StatusCode = 200
	result.Message = "success"
	return result, nil
}

func (s *Account) UpdateReader(ctx context.Context, req *v1.UpdateReaderRequest) (*v1.UpdateReaderReply, error) {
	result := &v1.UpdateReaderReply{}
	user := &User{}
	err := s.db.DB().Table(user.TableName()).Where("id = ? AND EXISTS ( SELECT 1 FROM fx_rbac_role_user WHERE ( user.id = rbac_role_user.user_id AND role_id IN ( SELECT id FROM fx_rbac_role WHERE ( identifier = 'reader' ) LIMIT 1 ) ) )", req.Id).First(user).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			result.StatusCode = 405
			result.Message = "user not found"
			return result, nil
		}
		return nil, err
	}
	timestamp := time.Now().Unix()
	update := make(map[string]any)
	if req.Mobile != nil {
		update["mobile"] = *req.Mobile
	}
	if req.Username != nil {
		update["username"] = *req.Username
	}
	if req.Email != nil {
		update["email"] = *req.Email
	}
	if req.RealName != nil {
		update["real_name"] = *req.RealName
	}
	if req.IdCardNo != nil {
		update["id_card_no"] = *req.IdCardNo
	}
	if req.BankNo != nil {
		update["bank_no"] = *req.BankNo
	}
	if req.BankOpen != nil {
		update["bank_open"] = *req.BankOpen
	}
	if req.Note != nil {
		update["note"] = *req.Note
	}
	if req.IsInternal != nil {
		update["is_internal"] = *req.IsInternal
	}
	if req.State != nil && *req.State != user.State {
		update["state"] = *req.State
		if req.StateReason != nil {
			update["state_reason"] = *req.StateReason
		}
	}
	if req.ReturnNormalAt != nil && *req.ReturnNormalAt > timestamp {
		update["return_normal_at"] = *req.ReturnNormalAt
	}
	if len(update) == 0 {
		result.StatusCode = 200
		result.Message = "success"
	}
	update["updated_at"] = timestamp
	err = s.db.DB().Table(user.TableName()).Where("id = ?", user.Id).Updates(update).Error
	if err != nil {
		return nil, err
	}
	result.StatusCode = 200
	result.Message = "success"
	return result, nil
}

func (s *Account) ReaderList(ctx context.Context, req *v1.ReaderListRequest) (*v1.ReaderListReply, error) {
	result := &v1.ReaderListReply{}
	get := s.db.DB().Model(&User{})
	get.Where("EXISTS ( SELECT 1 FROM fx_rbac_role_user WHERE ( user.id = rbac_role_user.user_id AND role_id IN ( SELECT id FROM fx_rbac_role WHERE ( identifier = 'reader' ) LIMIT 1 ) ) )")
	if req.Keyword != nil && *req.Keyword != "" {
		keyword := fmt.Sprintf("%%%s%%", *req.Keyword)
		get = get.Where("( username LIKE ? OR mobile LIKE ? OR real_name LIKE ? )", keyword, keyword, keyword)
	}
	get = get.Select("id, username, mobile, real_name")
	lists := make([]*User, 0)
	err := get.Find(&lists).Error
	if err != nil {
		return nil, err
	}
	result.Data = make([]*v1.ReaderListReply_Item, 0, len(lists))
	for _, v := range lists {
		result.Data = append(result.Data, &v1.ReaderListReply_Item{
			Id:       v.Id,
			Mobile:   v.Mobile,
			Username: v.Username,
			RealName: v.RealName,
		})
	}
	result.StatusCode = 200
	result.Message = "success"
	return result, nil
}

type approveSourcesItem1 struct {
	UserId           int64  `gorm:"column:user_id"`
	ApproveSourcesId int64  `gorm:"column:approve_sources_id"`
	IsInternal       int32  `gorm:"column:is_internal"`
	RealName         string `gorm:"column:real_name"`
}

func (s *Account) ApproveSourcesLists(ctx context.Context, req *v1.ApproveSourcesListsRequest) (*v1.ApproveSourcesListsReply, error) {
	result := &v1.ApproveSourcesListsReply{}
	source := &ApproveSources{}
	get := s.db.DB().Table(source.TableName())
	if req.MediaId != nil {
		get = get.Where("media_id = ?", req.GetMediaId())
	}
	if req.PlatformId != nil {
		get = get.Where("platform_id = ?", req.GetPlatformId())
	}
	if req.Level != nil {
		get = get.Where("level = ?", req.GetLevel())
	}
	if req.Opened != nil {
		get = get.Where("opened = ?", req.GetOpened())
	}
	subquery := "SELECT a.user_id, a.approve_sources_id, b.is_internal, b.real_name FROM fx_user_approve_sources a INNER JOIN user b ON a.user_id = b.id"
	subquery1 := subquery
	if req.Reader != nil && *req.Reader != "" {
		keyword := fmt.Sprintf("%%%s%%", req.GetReader())
		subquery1 = subquery1 + " WHERE ( b.real_name LIKE ? )"
		prepare := fmt.Sprintf("EXISTS ( SELECT 1 FROM ( %s ) a WHERE ( approve_sources.id = a.approve_sources_id ) )", subquery1)
		get = get.Where(prepare, keyword)
	}
	err := get.Count(&result.Total).Error
	if err != nil {
		return nil, err
	}
	page := getPage(req.GetPage())
	limit := getLimit(req.GetLimit())
	offset := getOffset(page, limit)
	get = get.Order("id ASC").Limit(limit).Offset(offset)
	lists := make([]*ApproveSources, 0)
	err = get.Find(&lists).Error
	if err != nil {
		return nil, err
	}
	length := len(lists)
	idLists := make([]int64, 0, length)
	items := make([]*v1.ApproveSourcesListsReply_Item, 0, length)
	for _, v := range lists {
		idLists = append(idLists, v.Id)
		tmp := &v1.ApproveSourcesListsReply_Item{
			Id:                 v.Id,
			MediaId:            v.MediaId,
			Media:              v.Media,
			PlatformId:         v.PlatformId,
			Platform:           v.Platform,
			ChannelName:        v.ChannelName,
			SourceLabel:        v.SourceLabel,
			SourceType:         int32(v.SourceType),
			Frequency:          v.Frequency,
			Level:              int32(v.Level),
			AutoMachine:        int32(v.AutoMachine),
			AutoMachineImage:   int32(v.AutoMachineImage),
			AutoMachineVideo:   int32(v.AutoMachineVideo),
			ManualMachine:      int32(v.ManualMachine),
			ManualMachineImage: int32(v.ManualMachineImage),
			ManualMachineVideo: int32(v.ManualMachineVideo),
			Opened:             int32(v.Opened),
			IsPush:             int32(v.IsPush),
			CreatedAt:          v.CreatedAt,
		}
		items = append(items, tmp)
	}
	prepare := subquery + " WHERE ( a.approve_sources_id IN (?) )"
	readerList := make([]*approveSourcesItem1, 0)
	if err = s.db.DB().Raw(prepare, idLists).Scan(&readerList).Error; err != nil {
		return nil, err
	}
	mp := make(map[int64][]*v1.ApproveSourcesListsReply_Item_Reader)
	for _, v := range readerList {
		if _, ok := mp[v.ApproveSourcesId]; !ok {
			mp[v.ApproveSourcesId] = make([]*v1.ApproveSourcesListsReply_Item_Reader, 0, 1)
		}
		tmp := &v1.ApproveSourcesListsReply_Item_Reader{
			Id:         v.UserId,
			RealName:   v.RealName,
			IsInternal: v.IsInternal,
		}
		mp[v.ApproveSourcesId] = append(mp[v.ApproveSourcesId], tmp)
	}
	for _, v := range items {
		if w, ok := mp[v.Id]; !ok {
			v.Reader = make([]*v1.ApproveSourcesListsReply_Item_Reader, 0)
		} else {
			v.Reader = w
		}
	}
	result.StatusCode = 200
	result.Message = "success"
	result.Data = items
	return result, nil
}

func (s *Account) ApproveSourcesUpdate(ctx context.Context, req *v1.ApproveSourcesUpdateRequest) (*v1.ApproveSourcesUpdateReply, error) {
	result := &v1.ApproveSourcesUpdateReply{}
	table := &ApproveSources{}
	err := s.db.DB().Table(table.TableName()).Where("id = ?", req.Id).First(table).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			result.StatusCode = 405
			result.Message = "数据不存在"
			return result, nil
		}
		return nil, err
	}
	timestamp := time.Now().Unix()
	update := make(map[string]any)
	if req.Opened != nil {
		update["opened"] = *req.Opened
	}
	if len(update) == 0 {
		result.StatusCode = 200
		result.Message = "success"
	}
	update["updated_at"] = timestamp
	err = s.db.DB().Table(table.TableName()).Where("id = ?", table.Id).Updates(update).Error
	if err != nil {
		return nil, err
	}
	result.StatusCode = 200
	result.Message = "success"
	return result, nil
}

func (s *Account) ApproveSourcesUpdateReader(ctx context.Context, req *v1.ApproveSourcesUpdateReaderRequest) (*v1.ApproveSourcesUpdateReaderReply, error) {
	result := &v1.ApproveSourcesUpdateReaderReply{}
	table := &ApproveSources{}
	err := s.db.DB().Table(table.TableName()).Where("id = ?", req.Id).First(table).Error
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			result.StatusCode = 405
			result.Message = "数据不存在"
			return result, nil
		}
		return nil, err
	}
	timestamp := time.Now().Unix()
	length := len(req.GetReader())
	lists := make([]*UserApproveSources, 0, length)
	for _, v := range req.GetReader() {
		tmp := &UserApproveSources{
			ApproveSourcesId: req.Id,
		}
		tmp.UserId = v
		tmp.CreatedAt = timestamp
		tmp.UpdatedAt = timestamp
		lists = append(lists, tmp)
	}
	err = s.db.DB().Transaction(func(tx *gorm.DB) error {
		model := &UserApproveSources{}
		if err = tx.Model(model).Where("approve_sources_id = ?", req.Id).Unscoped().Delete(model).Error; err != nil {
			return err
		}
		if len(lists) > 0 {
			if err = tx.Model(model).Create(lists).Error; err != nil {
				return err
			}
		}
		return nil
	})
	result.StatusCode = 200
	result.Message = "success"
	return result, nil
}

func (s *Account) ApproveSourcesItems(ctx context.Context, req *v1.ApproveSourcesItemsRequest) (*v1.ApproveSourcesItemsReply, error) {
	result := &v1.ApproveSourcesItemsReply{}
	source := &ApproveSources{}
	get := s.db.DB().Table(source.TableName())
	switch req.GetCategory() {
	case 1:
		get = get.Select("DISTINCT media_id, media")
	case 2:
		get = get.Select("DISTINCT platform_id, platform")
	case 3:
		get = get.Select("DISTINCT channel_name")
	default:
		result.StatusCode = 500
		result.Message = "请求不合法"
		return result, nil
	}
	// get = get.Order("id ASC")
	lists := make([]*ApproveSources, 0)
	err := get.Find(&lists).Error
	if err != nil {
		return nil, err
	}
	length := len(lists)
	idLists := make([]int64, 0, length)
	items := make([]*v1.ApproveSourcesItemsReply_Item, 0, length)
	for _, v := range lists {
		idLists = append(idLists, v.Id)
		tmp := &v1.ApproveSourcesItemsReply_Item{
			MediaId:     v.MediaId,
			Media:       v.Media,
			PlatformId:  v.PlatformId,
			Platform:    v.Platform,
			ChannelName: v.ChannelName,
		}
		items = append(items, tmp)
	}
	result.StatusCode = 200
	result.Message = "success"
	result.Data = items
	return result, nil
}

func (s *Account) ApproveSourcesAll(ctx context.Context, req *v1.ApproveSourcesAllRequest) (*v1.ApproveSourcesAllReply, error) {
	reply := &v1.ApproveSourcesAllReply{}
	model := &ApproveSources{}
	lists := make([]*ApproveSources, 0)
	err := s.db.DB().Model(model).Select("id, media_id, media, platform_id, platform, channel_name").Order("id").Find(&lists).Error
	if err != nil {
		return nil, err
	}
	items := make([]*v1.ApproveSourcesAllReply_Item, 0)
	for _, v := range lists {
		tmp := &v1.ApproveSourcesAllReply_Item{
			Id:          v.Id,
			MediaId:     v.MediaId,
			Media:       v.Media,
			PlatformId:  v.PlatformId,
			Platform:    v.Platform,
			ChannelName: v.ChannelName,
		}
		items = append(items, tmp)
	}
	reply.StatusCode = 200
	reply.Message = "success"
	reply.Data = items
	return reply, nil
}
